package roborally.model;
import java.io.*;
import java.util.*;

import roborally.gui.*;

public class Facade implements IFacade<Board, Robot, Wall, Battery, RepairKit, SurpriseBox> {
	//TODO: Prevent precondition violations for nominal methods (by checking before calling a method that its precondition holds)
	
	//non overriding private methods so there's not too much duplicate code in facade
	private void putElement(Board board, Position position, Element element)
	{
		try{
			board.putElement(position,element);
		}
		catch (IllegalArgumentException argExc){
			System.err.print(argExc.getMessage());
		}
		catch (IllegalStateException stateExc){
			System.err.print(stateExc.getMessage());
		}
	}
	private long getElementX(Element element) throws IllegalStateException {
		if(!element.isPlacedOnBoard()) throw new IllegalStateException("element not positioned on a board.");
		return element.getField().getPosition().getX();
	}
	private long getElementY(Element element) throws IllegalStateException {
		if(!element.isPlacedOnBoard()) throw new IllegalStateException("element not positioned on a board.");
		return element.getField().getPosition().getY();
	}
	private void pickUpItem(Robot robot, Item item){
		boolean b = robot.pickUpItem(item);
		if(!b) System.err.print("Item was not picked up.");
	}
	private void useItem(Robot robot, Item item){
		boolean b = robot.useItem(item);
		if(!b) System.err.print("Item was not used.");
	}
	public void dropItem(Robot robot, Item item) {
		boolean b = false; 
		try{
			b = robot.dropItem(item);
		}
		catch (IllegalArgumentException argExc){
			System.err.print(argExc.getMessage());
		}
		catch (IllegalStateException stateExc){
			System.err.print(stateExc.getMessage());
		}
		if(!b) System.err.print("Item was not present in possesions of robot.");
	}
	//Board related
	@Override
	public Board createBoard(long width, long height) {
		try{
			return new Board(width,height);
		}
		catch (IllegalArgumentException exc)
		{
			System.err.print(exc.getMessage());
			return null;
		}
	}
	@Override
	public void merge(Board board1, Board board2) {
		board1.merge(board2);
	}
	@Override
	public Set<Robot> getRobots(Board board) {
		return board.getRobots();
	}
	@Override
	public Set<Battery> getBatteries(Board board) {
		return board.getBatteries();
	}
	@Override
	public Set<Wall> getWalls(Board board) throws UnsupportedOperationException {
		return board.getWalls();
	}
	
	@Override
	public Set<RepairKit> getRepairKits(Board board) {
		return board.getRepairKits();
	}
	@Override
	public Set<SurpriseBox> getSurpriseBoxes(Board board) {
		return board.getSurpriseBoxes();
	}
	
	//Robot related
	@Override
	public Robot createRobot(int orientation, double initialEnergy) {
		if(EnergyAmount.isValidEnergy(new EnergyAmount(initialEnergy), Robot.EMAX_MAX))
			return new Robot(Orientation.values()[orientation],new EnergyAmount(initialEnergy));
		System.err.print("Invalid initialEnergy.");
		return null;
	}
	@Override
	public void putRobot(Board board, long x, long y, Robot robot) {
		putElement(board, new Position(x,y), robot);
	}
	@Override
	public long getRobotX(Robot robot) throws IllegalStateException {
		return getElementX(robot);
	}
	@Override
	public long getRobotY(Robot robot) throws IllegalStateException {
		return getElementY(robot);
	}
	@Override
	public int getOrientation(Robot robot) {
		return robot.getOrientation().ordinal();
	}
	@Override
	public double getEnergy(Robot robot) {
		return robot.getEnergy().getAmount();
	}
	@Override
	public void move(Robot robot) {
		if(!robot.hasEnergyToMove()){
			System.err.print("Insufficient energy.");
			return;
		}
		robot.move();
	}
	@Override
	public void turn(Robot robot) {
		if(EnergyAmount.isValidEnergy(robot.getEnergy().subtract(Robot.ECOST_TURN), robot.getMaxEnergy()))
			robot.turn(true);
		else
			System.err.print("Insufficient energy.");
	}
	
	@Override
	public void pickUpBattery(Robot robot, Battery battery) {
		pickUpItem(robot,battery);
	}
	@Override
	public void useBattery(Robot robot, Battery battery) {
		useItem(robot,battery);
		
	}
	@Override
	public void dropBattery(Robot robot, Battery battery) {
		dropItem(robot, battery);
	}
	@Override
	public void pickUpRepairKit(Robot robot, RepairKit repairKit) {
		pickUpItem(robot,repairKit);	
	}
	@Override
	public void useRepairKit(Robot robot, RepairKit repairKit) {
		useItem(robot, repairKit);
	}
	@Override
	public void dropRepairKit(Robot robot, RepairKit repairKit) {
		dropItem(robot, repairKit);
	}
	@Override
	public void pickUpSurpriseBox(Robot robot, SurpriseBox surpriseBox) {
		pickUpItem(robot, surpriseBox);
	}
	@Override
	public void useSurpriseBox(Robot robot, SurpriseBox surpriseBox) {
		useItem(robot, surpriseBox);
	}
	@Override
	public void dropSurpriseBox(Robot robot, SurpriseBox surpriseBox) {
		dropItem(robot, surpriseBox);
	}
	@Override
	public void transferItems(Robot from, Robot to) {
		boolean b = from.transferItemsTo(to);
		if(!b) System.err.print("No items were transfered.");
	}
	
	@Override
	public int isMinimalCostToReach17Plus() {
		return 0;
	}
	@Override
	public int isMoveNextTo18Plus() {
		return 0;
	}
	@Override
	public double getMinimalCostToReach(Robot robot, long x, long y) {
		EnergyAmount energyRequired = robot.getEnergyRequiredToReach(new Position(x,y));
		if(robot.getEnergy().compareTo(energyRequired) < 0)
			return -1;
		return energyRequired.getAmount();
	}
	@Override
	public void moveNextTo(Robot robot, Robot other) {
		Robot.moveNextTo(robot,other);
		
	}
	@Override
	public void shoot(Robot robot) throws UnsupportedOperationException {
		robot.shoot();
	}
	
	//PROGRAM related
	@Override
	public int loadProgramFromFile(Robot robot, String path) {
		try{
			robot.loadProgram(path);	
		}
		catch (IOException exc){ 
			System.err.print(exc.getMessage());
			return -1; 
		}
		catch (IllegalArgumentException exc){ 
			System.err.print("Invalid Program: " + exc.getMessage());
			return -1; 
		}
		return 0;
	}
	@Override
	public int saveProgramToFile(Robot robot, String path) {
		try {
			robot.getProgram().saveProgram(path);
		}catch (IOException ioexc){
			System.err.println(ioexc.getMessage());
			return -1;
		}
		return 0;
	}
	@Override
	public void prettyPrintProgram(Robot robot, Writer writer) {
		try{	
			writer.write(robot.getProgram().getProgramString());
		}
		catch (IOException ioexc){ System.err.print(ioexc.getMessage()); }
	}
	@Override
	public void stepn(Robot robot, int n) {
		robot.getProgram().stepn(n);
	}
	
	//Battery related
	@Override
	public Battery createBattery(double initialEnergy, int weight) {
		return new Battery(new EnergyAmount(initialEnergy), weight);
	}
	@Override
	public void putBattery(Board board, long x, long y, Battery battery) {
		putElement(board, new Position(x,y), battery);
	}
	@Override
	public long getBatteryX(Battery battery) throws IllegalStateException {
		return getElementX(battery);
	}
	@Override
	public long getBatteryY(Battery battery) throws IllegalStateException {
		return getElementY(battery);
	}
	
	//Wall related
	@Override
	public Wall createWall() throws UnsupportedOperationException {
		return new Wall(); //returns a wall that is not positioned on a board yet.
	}
	@Override
	public void putWall(Board board, long x, long y, Wall wall) throws UnsupportedOperationException {
		putElement(board, new Position(x,y), wall);
	}
	@Override
	public long getWallX(Wall wall) throws IllegalStateException,UnsupportedOperationException {
		return getElementX(wall);
	}
	@Override
	public long getWallY(Wall wall) throws IllegalStateException,UnsupportedOperationException {
		return getElementY(wall);
	}
	//RepairKit related
	@Override
	public RepairKit createRepairKit(double repairAmount, int weight) {
		return new RepairKit(new EnergyAmount(repairAmount), weight);
	}
	@Override
	public void putRepairKit(Board board, long x, long y, RepairKit repairKit) {
		putElement(board, new Position(x,y), repairKit);
	}
	@Override
	public long getRepairKitX(RepairKit repairKit) throws IllegalStateException {
		return getElementX(repairKit);
	}
	@Override
	public long getRepairKitY(RepairKit repairKit) throws IllegalStateException {
		return getElementY(repairKit);
	}
	//SurpriseBox related
	@Override
	public SurpriseBox createSurpriseBox(int weight) {
		return new SurpriseBox(weight);
	}
	@Override
	public void putSurpriseBox(Board board, long x, long y, SurpriseBox surpriseBox) {
		putElement(board, new Position(x,y), surpriseBox);
	}
	@Override
	public long getSurpriseBoxX(SurpriseBox surpriseBox) throws IllegalStateException {
		return getElementX(surpriseBox);
	}
	@Override
	public long getSurpriseBoxY(SurpriseBox surpriseBox) throws IllegalStateException {
		return getElementY(surpriseBox);
	}

}
